/*
 * Copyright 2020 The Emscripten Authors.  All rights reserved.
 * Emscripten is available under two separate licenses, the MIT license and the
 * University of Illinois/NCSA Open Source License.  Both these licenses can be
 * found in the LICENSE file.
 */
// This file runs a handful of neon instructions with some test values.

#include <cassert>
#include <stdio.h>
#include <string.h>
#include <arm_neon.h>

static int
test_simde_vaddq_s32 () {
        struct {
                int32_t a[4];
                int32_t b[4];
                int32_t r[4];
        } test_vec[] = {
                        { { -446537815, -222544457,  1358712669,  1039645779 },
                          { -1526568976,  1435568682,  1254287845,  1972851916 },
                          { -1973106791,  1213024225, -1681966782, -1282469601 } },
                        { {  1801189811, -1805838537, -1176151706,  116907030 },
                          {  1856764227, -389858301, -231569882, -1922577958 },
                          { -637013258,  2099270458, -1407721588, -1805670928 } },
                        { { -84360509,  1083070169, -973507665, -1815351216 },
                          { -335448343, -640367181,  634062411, -1817038128 },
                          { -419808852,  442702988, -339445254,  662577952 } },
                        { { -829576203, -1341252863, -545912689, -495828488 },
                          {  1808692408, -2075876551,  2041122729,  906779457 },
                          {  979116205,  877837882,  1495210040,  410950969 } },
                        { {  117807366,  1152914357,  337849883,  670471535 },
                          {  1116914697,  298243687,  663384037, -782395445 },
                          {  1234722063,  1451158044,  1001233920, -111923910 } },
                        { { -438738384, -1859546762,  782585023, -346645534 },
                          { -953227168, -1529285441,  801858404,  704653818 },
                          { -1391965552,  906135093,  1584443427,  358008284 } },
                        { {  17881483,  697514346,  1750612102,  877899476 },
                          {  1442546070, -621095818,  822723895,  2052786670 },
                          {  1460427553,  76418528, -1721631299, -1364281150 } },
                        { {  1299934179,  729222821,  462671687,  324003453 },
                          { -563525016,  1438147358,  394707240, -1382948150 },
                          {  736409163, -2127597117,  857378927, -1058944697 } }
        };

        for (size_t i = 0 ; i < (sizeof(test_vec) / sizeof(test_vec[0])) ; i++) {
                int32x4_t a = vld1q_s32(test_vec[i].a);
                int32x4_t b = vld1q_s32(test_vec[i].b);
                int32x4_t r = vaddq_s32(a, b);
                int32_t r_[4];
                vst1q_s32(r_, r);
                assert(memcmp(r_, test_vec[i].r, sizeof(int32_t) * 4) == 0);
        }

        return 0;
}

static int
test_simde_vsubq_s16 () {
        struct {
                int16_t a[8];
                int16_t b[8];
                int16_t r[8];
        } test_vec[] = {
                        { {  31318,  4134, -6723,  21335, -25763,  17298, -9065,  1079 },
                          {  19197,  1228, -7522, -5302, -1743, -13071,  8924,  13041 },
                          {  12121,  2906,  799,  26637, -24020,  30369, -17989, -11962 } },
                        { {  6045,  23106, -25859,  23213,  16437, -13155, -10980,  6608 },
                          { -25569, -17123,  26495, -20312, -26016,  15740,  28348,  22895 },
                          {  31614, -25307,  13182, -22011, -23083, -28895,  26208, -16287 } },
                        { { -19835, -32077,  24908, -32291,  31393, -17075,  7503,  28374 },
                          { -3143,  14379, -11174, -17431,  25966,  11000,  26579,  22916 },
                          { -16692,  19080, -29454, -14860,  5427, -28075, -19076,  5458 } },
                        { {  14105,  26075, -18280,  14822,  13107, -32010, -13232,  2801 },
                          {  7359,  6722,  11248,  24277, -12911,  25737,  3380,  20157 },
                          {  6746,  19353, -29528, -9455,  26018,  7789, -16612, -17356 } },
                        { { -26300, -8781, -26031, -31722,  3533,  7687, -1831, -26328 },
                          {  27156,  1459, -30570,  10083, -5035, -30325,  18937,  16087 },
                          {  12080, -10240,  4539,  23731,  8568, -27524, -20768,  23121 } },
                        { { -29726,  13083,  12581, -3400, -16578,  6160,  14519, -13391 },
                          {  25763,  14800,  13548,  16736, -5344,  6858, -24012,  5720 },
                          {  10047, -1717, -967, -20136, -11234, -698, -27005, -19111 } },
                        { {  29485,  21066,  676, -7356,  21953,  30971, -21363,  12355 },
                          {  5136, -919, -14008,  26685,  1973, -5758, -9559, -10752 },
                          {  24349,  21985,  14684,  31495,  19980, -28807, -11804,  23107 } },
                        { {  19021, -3544,  27980,  3541, -12094,  20357, -14212, -29568 },
                          { -5668,  9352, -14925,  26765,  4044,  30289,  20970,  14156 },
                          {  24689, -12896, -22631, -23224, -16138, -9932,  30354,  21812 } }
        };

        for (size_t i = 0 ; i < (sizeof(test_vec) / sizeof(test_vec[0])) ; i++) {
                int16x8_t a = vld1q_s16(test_vec[i].a);
                int16x8_t b = vld1q_s16(test_vec[i].b);
                int16x8_t r = vsubq_s16(a, b);

                int16_t r_[8];
                vst1q_s16(r_, r);
                assert(memcmp(r_, test_vec[i].r, sizeof(int16_t) * 8) == 0);
        }
        return 0;
}

static int
test_simde_vmulq_u32 () {
        struct {
                uint32_t a[4];
                uint32_t b[4];
                uint32_t r[4];
        } test_vec[] = {
                        { { 838207622, 3405383108, 2377386269, 4159190503 },
                          { 2224025479, 3989376773, 3953058906, 1658203612 },
                          { 3851262122, 2552010964, 1750009906, 1512172932 } },
                        { { 4187212853, 4005793489, 2138863511, 880174253 },
                          { 1505297747, 1850114580, 1113187430, 1185165073 },
                          { 3442818607, 3505605204, 664347178, 86798973 } },
                        { { 3426695163, 1572537029, 668743546, 4015739547 },
                          { 1799885911, 4242116246, 2201891698, 1590288994 },
                          { 24355405, 3961120110, 2091516500, 93680982 } },
                        { { 3744073754, 2218583306, 3098220572, 3248949098 },
                          { 2989289243, 4021159549, 2608000313, 3925425103 },
                          { 1064771774, 31855074, 870468156, 3302911158 } },
                        { { 1305027651, 651298057, 2296282398, 2689238404 },
                          { 4048713332, 3051356284, 3176223469, 3500558989 },
                          { 2790346844, 2459594844, 3062591686, 2805707188 } },
                        { { 2015194990, 2476666741, 4045110381, 1972462849 },
                          { 1483138012, 3507373796, 630087576, 418723241 },
                          { 2297318536, 2355970612, 552883128, 2027545257 } },
                        { { 428872356, 1873554946, 2875246762, 153154093 },
                          { 3110242005, 1686859980, 2022250959, 4086333006 },
                          { 1432967796, 342591896, 433380214, 4018925494 } },
                        { { 2450268304, 4160862542, 2929943169, 699974484 },
                          { 367204937, 1501130378, 3604021895, 298410624 },
                          { 2423515408, 577464332, 2688480263, 2283524608 } },
        };

        for (size_t i = 0 ; i < (sizeof(test_vec) / sizeof(test_vec[0])) ; i++) {
                uint32x4_t a = vld1q_u32(test_vec[i].a);
                uint32x4_t b = vld1q_u32(test_vec[i].b);
                uint32x4_t r = vmulq_u32(a, b);
                uint32_t r_[4];
                vst1q_u32(r_, r);
                assert(memcmp(r_, test_vec[i].r, sizeof(uint32_t) * 4) == 0);
        }
        return 0;
}

static int
test_simde_vbslq_s8 () {
        struct {
                uint8_t a[16];
                int8_t b[16];
                int8_t c[16];
                int8_t r[16];
        } test_vec[] = {
                        { { 136, 230, 97, 0, 20, 50, 138, 231,
                            UINT8_MAX, 18, 190, 185, 181, 25, 53, 6 },
                          {  60, -30,  93, -89, -3,  121,  83, -123,
                             102, -115, -97, -39, -104,  82, -24,  32 },
                          {  56,  73,  32,  76,  123, -85,  51,  122,
                             -67, -15,  51,  114,  11,  104,  120,  71 },
                          {  56, -21,  65,  76,      INT8_MAX, -71,  51, -99,
                             102, -31, -97, -37, -102,  112,  104,  65 } },
                        { { 74, 214, 238, 71, 79, 65, 204, 181,
                            207, 107, 142, 103, 189, 118, 136, 245 },
                          { -65, -88,  65,  58,  83,  117, -76,  17,
                            102, -25, -125,  113,  79, -4, -72, -103 },
                          { -46, -90, -32,  33, -24, -84, -42, -73,
                            23,  100,  30, -44, -37, -90, -55, -102 },
                          { -102, -96,  64,  34, -29, -19, -106,  19,
                            86,  103, -110, -15,  79, -12, -55, -101 } },
                        { { 79, 11, 213, 162, 128, 137, 179, 230,
                            113, 55, 88, 192, 51, 16, 90, 5 },
                          { -73,  58,  38, -97, -25, -4,  86, -2,
                            96,  116, -45,  59,  27, -100, -42,  106 },
                          { -89, -85,  12,  39,  52, -64,  14, -91,
                            -9,  102,  102,  42,  118, -64,  47,  45 },
                          { -89, -86,  12, -121, -76, -56,  30, -25,
                            -26,  116,  118,  42,  87, -48,  119,  40 } },
                        { { 250, 85, 204, 225, 81, 34, 224, 177,
                            151, 179, 237, 178, 79, 195, 28, 247 },
                          {  110,  40,  30, -94, -24,  44,  72, -33,
                             -110, -82,  9,  9,  110,  56,  54,  104 },
                          { -115,  3,  74, -34,  37,  42, -112, -68,
                            -35,  125,  110,  44,  64, -118,  35, -82 },
                          {  111,  2,  14, -66,  100,  40,  80, -99,
                             -38, -18,  11,  12,  78,  8,  55,  104 } },
                        { { 179, 66, 80, 155, 110, 152, 123, 1,
                            70, 132, 10, 180, 189, 64, 29, 74 },
                          {  67,  103,  41,  105, -111, -71,  37,  110,
                             54, -108, -102,  118,  30, -66,  36, -47 },
                          {  0,  116,  109,  110,  13, -24,  111,  83,
                             108,  121,  8,  41, -70,  37,  116, -3 },
                          {  3,  118,  45,  109,  1, -8,  37,  82,
                             46, -3,  10,  61,  30,  37,  100, -11 } },
                        { { 140, 157, 102, 29, 86, 140, 139, 140,
                            32, 37, 2, 62, 227, 38, 16, 227 },
                          { -102,  125,  82, -89,  101, -63, -5, -47,
                            59,  3, -5, -11,  40,  111, -14, -76 },
                          {  12,  89, -47,  98, -27,  92, -18,  5,
                             -127, -16,  67,  101,  22,  83,  72, -80 },
                          { -120,  93, -45,  103, -27, -48, -17, -127,
                            -95, -47,  67,  117,  52,  119,  88, -80 } },
                        { { 208, 154, 88, 53, 92, 83, 7, 151,
                            86, 2, 140, 126, 113, 126, 50, 125 },
                          { -41,  3, -33, -68,  95, -51, -63, -32,
                            -67,  5,  69, -45,  88, -114, -125,  41 },
                          {  40, -37,  94, -124,  46,  101,  27, -124,
                             103, -89,  2, -40,  38,  52,  85, -3 },
                          { -8,  67,  94, -76,  126,  101,  25,      INT8_MIN,
                            53, -91,  6, -46,  86,  14,  71, -87 } },
                        { { 55, 52, 186, 150, 1, 123, 119, 190,
                            128, 188, 145, 217, 74, 21, 2, 115 },
                          { -16,  96, -9,  31, -58,  19, -93,  45,
                            -70, -90,  6, -32, -38,  91, -34,  18 },
                          { -112, -104, -88, -111,  19,  31,  80, -108,
                            -36, -31,  109,  38, -10,  111, -103, -25 },
                          { -80, -88, -78,  23,  18,  23,  35,  44,
                            -36, -27,  108, -26, -2,  123, -101, -106 } },
        };

        for (size_t i = 0 ; i < (sizeof(test_vec) / sizeof(test_vec[0])) ; i++) {
                uint8x16_t a = vld1q_u8(test_vec[i].a);
                int8x16_t b = vld1q_s8(test_vec[i].b);
                int8x16_t c = vld1q_s8(test_vec[i].c);
                int8x16_t r = vbslq_s8(a, b, c);

                int8_t r_[16];
                vst1q_s8(r_, r);
                assert(memcmp(r_, test_vec[i].r, sizeof(int8_t) * 16) == 0);

        }

        return 0;
}

static int
test_simde_vshl_s64 () {
  struct {
    int64_t a[1];
    int64_t b[1];
    int64_t r[1];
  } test_vec[] = {
    { { -4131760704340726406 },
      { -51 },
      { -1835 } },
    { {  4795347804945835666 },
      { -8617733424538507080 },
      {  0 } },
    { { -2987477723010756817 },
      { -49 },
      { -5307 } },
    { { -834069088546172233 },
      {  62 },
      { -4611686018427387904 } },
    { { -3249651920531950297 },
      {  2418075451758470935 },
      {  7338760155284111360 } },
    { {  5243402359250859729 },
      { -47 },
      {  37256 } },
    { { -5953442574285360237 },
      {  33 },
      { -1003907829020491776 } },
    { {  7218011237552599699 },
      { -36 },
      {  105035887 } },
  };

  for (size_t i = 0 ; i < (sizeof(test_vec) / sizeof(test_vec[0])) ; i++) {
    int64x1_t a = vld1_s64(test_vec[i].a);
    int64x1_t b = vld1_s64(test_vec[i].b);
    int64x1_t r = vshl_s64(a, b);
    int64_t r_[1];
    vst1_s64(r_, r);
    assert(memcmp(r_, test_vec[i].r, sizeof(int64_t) * 1) == 0);
  }

  return 0;
}

int main() {
        printf("Testing NEON Wasm SIMD\n");
        test_simde_vaddq_s32();
        test_simde_vsubq_s16();
        test_simde_vmulq_u32();
        test_simde_vbslq_s8();
        test_simde_vshl_s64();
        printf("Success!\n");
}
